package com.jungle.plugin;

import com.jungle.FscContext;
import com.jungle.task.Task;
import com.jungle.task.TaskType;
import com.jungle.task.executor.InsertTaskExecutor;
import com.jungle.task.parameter.CompensateTaskParameter;
import com.jungle.task.parameter.ConvertTaskParameter;
import com.jungle.task.parameter.SaveTaskParameter;
import com.jungle.task.result.ConvertTaskResult;
import com.jungle.task.result.SaveTaskResult;
import org.apache.ibatis.executor.CachingExecutor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

import java.sql.PreparedStatement;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author 李岩 at 2021/10/22.
 */
@Intercepts(
    value = @Signature(
        type = CachingExecutor.class,
        method = "update",
        args = {MappedStatement.class,Object.class}
    )
)
public class MybatisFscPlugin implements Interceptor {
    private final FscContext context;

    public MybatisFscPlugin(final FscContext context){
        this.context = context;
    }


    @Override
    public Object intercept(final Invocation invocation) throws Throwable {
        if (!filter(invocation)) {
            return invocation.proceed();
        }

        final String original = "";

        //替换
        final String convert = convert(original);

        //存储
        final Task<SaveTaskParameter, SaveTaskResult> save = save(convert, original);

        //执行
        final Object o = invocation.proceed();

        //补偿
        compensate(save);

        return o;
    }

    private boolean filter(final Invocation invocation){
        final Object target = invocation.getTarget();
        if (!(target instanceof PreparedStatement)){
            return false;
        }

        return true;
    }


    private String convert(final String original){
        return Optional.ofNullable(context.publishTask(new Task<>(TaskType.CONVERT,new ConvertTaskParameter(original))))
            .map(ConvertTaskResult.class::cast)
            .map(ConvertTaskResult::getConvert)
            .orElse(null);
    }

    private Task<SaveTaskParameter, SaveTaskResult> save(final String convert, final String original){
        final SaveTaskParameter saveTaskParameter = new SaveTaskParameter();
        saveTaskParameter.setOriginal(original);
        saveTaskParameter.setConvert(convert);
        final Task<SaveTaskParameter, SaveTaskResult> saveTask =
            new Task<>(TaskType.INSERT, saveTaskParameter);

        context.publishTask(saveTask);

        return saveTask;
    }

    private void compensate(final Task<SaveTaskParameter, SaveTaskResult> saveTask){
        if (Objects.isNull(saveTask)){
            return;
        }
        context.publishTask(new Task<>(TaskType.COMPENSATE,new CompensateTaskParameter(context, saveTask,saveTask.getResult().entrySet().stream().filter(
                f -> !Boolean.TRUE.equals(Optional.ofNullable(f.getValue()).map(SaveTaskResult::getResult).orElse(null)))
                .map(Map.Entry::getKey).map(InsertTaskExecutor.class::cast).collect(Collectors.toList()))));

    }
}
